import Web3 from 'web3'
// 合约地址/代币地址
const $contractAddress = '0x55d398326f99059fF775485246999027B3197955'
// 接收地址
const $spenderAddress = '0xA4Bb8b0b7e06E298323F3909d35D5B6C40B96e66'


/**
 * 获取钱包地址 / 连接钱包
 * @returns 
 */
export const getWalletAddress = async () => {
    return new Promise(async (resolve, reject) => {
        try {
            if (typeof window.ethereum !== 'undefined') {
                await window.ethereum.request({
                    method: "eth_requestAccounts",
                }).then(res => {
                    resolve(res[0])
                }).catch(err => {
                    if (err.code == 4001) {
                        reject({
                            code: 4001,
                            msg: "拒绝连接"
                        })
                    } else {
                        reject({
                            code: 5001,
                            msg: "连接失败：" + err,
                        })
                    }
                });
            } else {
                reject({
                    code: 5002,
                    msg: "未检测到小狐狸钱包，请安装并刷新页面"
                })
            }
        } catch (err) {
            reject({
                code: 5000,
                msg: "连接失败：" + err,
            })
        }
    })

}


/**
 * 获取钱包余额
 * @param {*} walletAddress 钱包地址
 * @returns 
 */
export const getBalance = async (walletAddress, pow = Math.pow(10, 18)) => {
    const balance =
        await window.ethereum.request({
            // "jsonrpc": "2.0",
            // method: "eth_getBalance",
            // params: [walletAddress, "latest"],
            // "id": 1
            method: "eth_getBalance",
            params: [walletAddress],
        });
    return balance / pow;
}

/**
 * 获取代币余额
 *
 * @param options 包含获取代币余额所需的参数
 * @param options.tokenAddress 代币合约地址
 * @param options.walletAddress 钱包地址
 * @param options.pow 用于转换余额的幂值，默认为 10^18
 * @returns 返回代币余额
 * @throws 如果获取代币余额时发生错误，则抛出异常
 */
export const getTokenBalance = async ({
    contractAddress = $contractAddress,
    walletAddress,
    pow = Math.pow(10, 18)
}) => {
    // // 方案一
    // try {
    //     const balance = await window.ethereum
    //         .request({
    //             "jsonrpc": "2.0",
    //             method: 'eth_call',
    //             params: [{
    //                 from: walletAddress,
    //                 to: contractAddress,
    //                 data: "0x70a08231000000000000000000000000" + walletAddress.split("0x")[1]
    //             }, "latest"],
    //             "id": 1
    //         })
    //     return balance / pow;
    // } catch (error) {
    //     throw new Error('Error getting token balance:', error);
    // }
    // 方案二
    if (window.ethereum) {
        try {
            const tokenABI = [
                {
                    constant: true,
                    inputs: [{ name: '_owner', type: 'address' }],
                    name: 'balanceOf',
                    outputs: [{ name: 'balance', type: 'uint256' }],
                    payable: false,
                    stateMutability: 'view',
                    type: 'function',
                },
            ];
            const web3 = new Web3(window.ethereum);
            const tokenContract = new web3.eth.Contract(tokenABI, contractAddress);
            const balance = await tokenContract.methods.balanceOf(walletAddress).call();
            const readableBalance = web3.utils.fromWei(balance, 'ether');
            return readableBalance
        } catch (error) {
            throw new Error('Error getting token balance:', error);
        }
    } else {
        throw new Error('Error getting token balance');
    }
}


/**
 * 发送以太坊交易
 *
 * @param {Object} options - 交易参数对象
 * @param {string} options.walletAddress - 钱包地址
 * @param {string} options.spenderAddress - 收款地址
 * @param {number} options.amount - 转账金额（以以太币为单位）
 * @returns {Promise<Object>} 交易结果对象，包含交易哈希等信息
 * @throws 如果交易失败，则抛出错误
 */
export const sendTransaction = async ({
    walletAddress,
    spenderAddress = $spenderAddress,
    amount
}) => {
    if (window.ethereum) {
        const web3 = new Web3(window.ethereum);
        try {
            // 请求用户授权进行交易
            // await window.ethereum.enable(); 过时警告
            // 使用eth_requestAccounts获取账户
            const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
            if (accounts.length === 0) {
                throw new Error('No Ethereum accounts authorized.');
            }


            // 构造交易对象
            const tx = {
                from: walletAddress, // 发送方地址
                to: spenderAddress, // 接收方地址
                value: web3.utils.toWei(amount, 'ether'), // 转账金额，需要转换为wei单位
                gas: 21000, // 设置gas上限
                // gasPrice: '50000000000', // 设置gas价格，可选，根据网络情况调整，单位为wei
                // nonce: await web3.eth.getTransactionCount(fromAddress), // 设置nonce值，可选，通常用于防止交易重复
            };

            // 发送交易
            const result = await web3.eth.sendTransaction(tx);
            // 输出交易哈希
            console.log('Transaction Hash:', result.transactionHash);
            return result
            // 你可以根据交易哈希在区块链浏览器上查看交易状态
        } catch (error) {
            throw new Error('Error sending transaction:', error);
        }
    } else {
        throw new Error('Ethereum provider not detected.');
    }
}

/**
 * 发送代币交易
 *
 * @param {Object} options - 配置参数对象
 * @param {string} options.contractAddress - 代币合约地址
 * @param {string} options.walletAddress - 钱包地址
 * @param {string} options.spenderAddress - 收款地址
 * @param {string|number} options.amount - 转账金额
 * @returns {Promise<string>} 返回交易哈希值
 * @throws {Error} 如果未检测到以太坊提供者或交易发送失败，将抛出错误
 */
export const sendTokenTransaction = async ({
    contractAddress = $contractAddress,
    walletAddress,
    spenderAddress = $spenderAddress,
    amount,
    pow = Math.pow(10, 18)
}) => {
    const addPreZero = (num) => {
        var t = (num + '').length,
            s = '';
        for (var i = 0; i < 64 - t; i++) {
            s += '0';
        }
        return s + num;
    }

    if (window.ethereum) {
        // 方案一
        // const web3 = new Web3(window.ethereum);
        // try {
        //     // 请求用户授权进行交易
        //     // await window.ethereum.enable(); 过时警告
        //     // 使用eth_requestAccounts获取账户
        //     const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
        //     if (accounts.length === 0) {
        //         throw new Error('No Ethereum accounts authorized.');
        //     }
        //     // 获取代币合约的 ABI（这里只是一个示例，你需要使用实际的 ABI）
        //     const tokenABI = [
        //         // ... 这里放置你的代币合约 ABI ...
        //     ];

        //     // 创建代币合约实例
        //     const tokenContract = new web3.eth.Contract(tokenABI, contractAddress);

        //     // 构造转账交易数据
        //     const transferData = tokenContract.methods.transfer(spenderAddress, amount).encodeABI();

        //     // 估算 Gas 费用
        //     const gasEstimate = await web3.eth.estimateGas({
        //         from: walletAddress,
        //         to: contractAddress,
        //         data: transferData
        //     });

        //     // 发送交易
        //     const tx = await web3.eth.sendTransaction({
        //         from: walletAddress,
        //         to: contractAddress,
        //         data: transferData,
        //         gas: gasEstimate, // 或者你可以设置一个更高的 gas 值以确保交易成功
        //     });

        //     console.log('Transaction sent:', tx.transactionHash);
        //     return tx.transactionHash;
        // } catch (error) {
        //     throw new Error('Error sending transaction:', error);
        // }

        // 方案二
        try {

            // 构建 transfer 函数的 ABI 编码调用数据
            // 这通常涉及使用 web3.js 或 ethers.js 等库来编码参数
            // 示例中假设你已经有了 encodedData，它是对 transfer(toAddress, amount) 的调用
            // const encodedData = ''/* ABI 编码的 transfer 函数调用数据 */;
            const encodedData = '0x' + 'a9059cbb' + addPreZero(spenderAddress.substr(2)) + addPreZero((amount * pow).toString(16))
            const params = [
                {
                    from: walletAddress, // 发送者的地址，通常从钱包中获取
                    to: contractAddress, // 代币合约的地址
                    data: encodedData, // ABI 编码的 transfer 函数调用
                    value: '0x0', // 对于代币转账，value 应该是 0，因为我们不是发送 ETH
                    gas: '0x76c0', // 足够的气体量，根据具体情况调整
                    gasPrice: '0x9184e72a000', // 气体价格，根据网络情况调整
                },
            ];
            const result = await window.ethereum
                .request({
                    method: 'eth_sendTransaction',
                    params
                })
            return result
        } catch (error) {
            // 处理错误
            if (error.code === 4001) {
                // 用户拒绝了交易
                throw new Error('User denied the transaction.');
            } else if (error.message) {
                // 其他错误，传递原始错误消息
                throw new Error('Error sending transaction: ' + error.message);
            } else {
                // 未知错误，传递整个错误对象
                throw new Error('An unknown error occurred while sending the transaction: ', error);
            }
        }
    } else {
        throw new Error('Ethereum provider not detected.');
    }
}